using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.DataAccess.SQLDom;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.DataAccess.Common;

namespace T.FAS.Runtime.DataAccess.Engine
{
    /// <summary>
    /// 领域对象UpdateSQL构造策略
    /// </summary>
    class UpdateSqlBuildStrategy : BaseSqlBuildStrategy
    {
        /// <summary>
        /// 构造不包含数据的Sql（即SqlSchema）。
        /// </summary>
        /// <param name="sqls">Sql语句对象集合。</param>
        /// <param name="context">Sql构造的上下文信息。</param>
        public override SqlStatementCollection BuildTableSqlSchema(SqlBuildingContext context)
        {
            if (context == null)
                throw new FasException(DataAccessErrorCode.Update.NullValue, CommonStringClassEngine.SqlBuildStrategy_ContextCannotBeNull);

            var sqls = new SqlStatementCollection();

            var updateStatement = SQLStatementFactory.CreateSqlStatement(SqlStatementType.Update, context.DbType) as UpdateSqlStatement;
            updateStatement.SqlBuildingInfo.DataSource = context.DataSource;

            base.HandlingSqlStatement(updateStatement, context);
            sqls.Add(updateStatement);

            return sqls;
        }

        /// <summary>
        /// 在SqlSchema基础上，构造包含数据的Sql。
        /// </summary>
        /// <param name="sqls">Sql语句对象集合。</param>
        /// <param name="context">Sql构造的上下文信息。</param>
        public override void BuildTableSqlDetail(SqlStatementCollection sqls, SqlBuildingContext context)
        {
            var sql = sqls.FirstOrDefault(x => x.SqlBuildingInfo.TableName == context.TableName);
            if (sql == null) return;

            var updateSql = sql as UpdateSqlStatement;

            this.HandlingUpdateFields(updateSql, context);
            this.HandlingConditionInfoAddData(updateSql, context);
        }

        /// <summary>
        /// 设置UpdateSql的更新数据列。
        /// </summary>
        /// <param name="updateSql">UpdateSql对象。</param>
        /// <param name="context">Sql构造上下文信息。</param>
        protected virtual void HandlingUpdateFields(UpdateSqlStatement updateSql, SqlBuildingContext context)
        {
            var dataContext = context.DataContext.GetCurrentDataContextItem(context.Node.ID);

            if (dataContext == null || dataContext.Data == null || dataContext.Data.Count == 0)
            {
                updateSql = null;
                return;
            }
            if (updateSql.SqlBuildingInfo.CurrentSqlTable == null)
            {
                var tableName = context.TableName;
                updateSql.SqlBuildingInfo.CurrentSqlTable =
                    base.TryFindAndRegistSqlTable(tableName, tableName, tableName, tableName, updateSql.SqlBuildingInfo);
            }

            #region 最后修改信息处理
            //****FAS低代码改动：更新时，使用当前登录人更新CDO的最后修改人字段，使用服务端时间更新DO的时间戳字段
            //优先使用系统字段映射SystemFlag判断是否系统字段。
            CompositeNodeObjectElement lastModifierEle = context.Node.Elements.FirstOrDefault(ele => ele.ElementType == ElementType.Normal
                                                                                                        && (ele.DataColumn.SysColumnFlag == SysColumnFlag.LastModifier 
                                                                                                            || DataModelSystemConst.LastModifier == (ele.Alias ?? ele.Name)));
            DataColumn lastModifierCol = lastModifierEle?.GetDataColumn(context.Node);
            CompositeNodeObjectElement lastModifierNameEle = context.Node.Elements.FirstOrDefault(ele => ele.ElementType == ElementType.Normal 
                                                                                                        && (ele.DataColumn.SysColumnFlag == SysColumnFlag.LastModifierName 
                                                                                                            || DataModelSystemConst.LastModifierName == (ele.Alias ?? ele.Name)));
            DataColumn lastModifierNameCol = lastModifierNameEle?.GetDataColumn(context.Node);
            /*
             * 强行兼容老代码，同时支持将LastModifier的系统字段类型指向LastModifierName。实现LastModifier字段存储用户姓名的功能，
             * 但为了兼容老代码会造成lastModifierCol与lastModifierNameCol指向同一列的情况，
             * 使用如下的判断强行兼容。
             */
            if (lastModifierCol?.ColumnName == lastModifierNameCol?.ColumnName)
                lastModifierCol = null;

            CompositeNodeObjectElement timestampEle = null;
            DataColumn timestampCol = null;
            foreach (CompositeNodeObjectElement ele in context.Node.Elements.Where(e => e.ElementType == ElementType.Normal))
            {
                DataColumn col = ele.GetDataColumn(context.Node);
                if (col != null && col.IsTimestampField())
                {
                    timestampCol = col;
                    timestampEle = ele;
                }
            }

            #endregion

            foreach (string dataItem in dataContext.Data.Keys)
            {
                if (InternationalTools.IsI18NDataItemElementIDKey(dataItem, out string realFieldName, out string elementid))
                {
                    CompositeNodeObjectElement updateElement = context.Node.Elements.FirstOrDefault(i => i.ID == elementid);
                    DataColumn updateColumn = context.DataObject.Columns.FirstOrDefault(i => i.ID == updateElement.DataColumnID);

                    //统一数据访问服务掩码改造：没有权限的列跳过
                    if (context.ControlContext != null && !context.ControlContext.IsHasPermission(context.DataObject, updateColumn))
                    {
                        continue;
                    }
                    //字段设置
                    var field = new UpdateField();
                    field.IsUseFieldPrefix = false;
                    field.IsUseVarBinding = true;
                    field.Table = updateSql.SqlBuildingInfo.CurrentSqlTable;
                    field.FieldName = realFieldName;
                    updateSql.UpdateFields.ChildCollection.Add(field);
                    //字段值设置
                    var fieldValue = new UpdateValue();
                    fieldValue.Value = dataContext.Data[dataItem];
                    fieldValue.DataType = (int)updateElement.DataType;
                    if (updateColumn != null)
                        fieldValue.DbColumnDataType = updateColumn.GetDatabaseColumnType();
                    updateSql.UpdateValues.ChildCollection.Add(fieldValue);
                }
                else if (MaskTool.IsMaskDataItemElementIDKey(dataItem, out string realMaskFieldName, out string maskElementid))
                {
                    CompositeNodeObjectElement updateElement = context.Node.Elements.FirstOrDefault(i => i.ID == maskElementid);
                    DataColumn updateColumn = context.DataObject.Columns.FirstOrDefault(i => i.ID == updateElement.DataColumnID);

                    //统一数据访问服务掩码改造：没有权限的列跳过
                    if (context.ControlContext != null && !context.ControlContext.IsHasPermission(context.DataObject, updateColumn))
                    {
                        continue;
                    }
                    //字段设置
                    var field = new UpdateField();
                    field.IsUseFieldPrefix = false;
                    field.IsUseVarBinding = true;
                    field.Table = updateSql.SqlBuildingInfo.CurrentSqlTable;
                    field.FieldName = realMaskFieldName;
                    updateSql.UpdateFields.ChildCollection.Add(field);
                    //字段值设置
                    var fieldValue = new UpdateValue();
                    fieldValue.Value = dataContext.Data[dataItem];
                    fieldValue.DataType = (int)DataType.String;     //掩码字段只能为String类型
                    if (updateColumn != null)
                        fieldValue.DbColumnDataType = updateColumn.GetDatabaseColumnType();
                    updateSql.UpdateValues.ChildCollection.Add(fieldValue);
                }
                else
                {
                    CompositeNodeObjectElement updateElement = context.Node.Elements.FirstOrDefault(i => i.ID == dataItem);
                    DataColumn updateColumn = context.DataObject.Columns.FirstOrDefault(i => i.ID == updateElement.DataColumnID);

                    //统一数据访问服务掩码改造：没有权限的列跳过
                    if (context.ControlContext != null && !context.ControlContext.IsHasPermission(context.DataObject, updateColumn))
                    {
                        continue;
                    }

                    //****统一数据访问服务V1.2: 去掉update语句更新的主键列****
                    //****FAS低代码改动: 跳过时间戳的字段处理****
                    if (dataContext.PrimaryKeyData.ContainsKey(updateElement.ID)
                        || string.Equals(dataItem, timestampEle?.ID)
                        || string.Equals(dataItem, lastModifierEle?.ID)
                        || string.Equals(dataItem, lastModifierNameEle?.ID))
                    {
                        //updateSql.PrimaryKeys.ChildCollection.Add();
                        continue;
                    }

                    //字段设置
                    var field = new UpdateField();
                    field.IsUseFieldPrefix = false;
                    field.IsUseVarBinding = true;
                    field.Table = updateSql.SqlBuildingInfo.CurrentSqlTable;
                    field.FieldName = updateColumn.ColumnName;
                    updateSql.UpdateFields.ChildCollection.Add(field);
                    //字段值设置
                    var fieldValue = new UpdateValue();
                    fieldValue.Value = dataContext.Data[dataItem];
                    fieldValue.DataType = (int)updateElement.DataType;
                    fieldValue.DbColumnDataType = updateColumn.GetDatabaseColumnType();
                    updateSql.UpdateValues.ChildCollection.Add(fieldValue);
                }
            }

            if (timestampCol != null)   //没有时间戳字段，暂定默认不处理
            {
                /****************时间戳字段设置 应用服务器时间**************/
                var timestampField = new UpdateField();
                timestampField.IsUseFieldPrefix = false;
                timestampField.IsUseVarBinding = true;
                timestampField.Table = updateSql.SqlBuildingInfo.CurrentSqlTable;
                timestampField.FieldName = timestampCol.ColumnName;
                updateSql.UpdateFields.ChildCollection.Add(timestampField);

                //时间戳字段值设置
                //var dateTime = DateTime.Now;  //保留毫秒时间精度
                //var dateTime = DateTime.Now.TruncateFromMilliseconds();    //去掉毫秒时间精度
                var dateTime = DACContextHelper.GetTimestamp(); //线程上下文中获取    

                var timestampValue = new UpdateValue();
                timestampValue.Value = dateTime;
                timestampValue.DataType = (int)timestampEle.DataType;
                timestampValue.DbColumnDataType = timestampCol.GetDatabaseColumnType();
                updateSql.UpdateValues.ChildCollection.Add(timestampValue);

                /****************时间戳字段设置 数据库服务器时间**************/
                //var timestampField = new UpdateField();
                //timestampField.IsUseFieldPrefix = false;
                //timestampField.IsUseVarBinding = false;
                //timestampField.Table = updateSql.SqlBuildingInfo.CurrentSqlTable;
                //timestampField.FieldName = timestampCol.ColumnName;
                //updateSql.UpdateFields.ChildCollection.Add(timestampField);

                ////时间戳字段值设置
                //var dbType = updateSql.SqlBuildingInfo.CurrentNode.DataObject.Database.DbType;
                //var databaseType = (DatabaseType)Enum.Parse(typeof(DatabaseType), dbType);
                //string dateFunc = DatabaseTool.GetDefaultDateFunc(databaseType);
                //var timestampValue = new UpdateValue();
                //timestampValue.Value = dateFunc;
                //timestampValue.DataType = (int)timestampEle.DataType;
                //updateSql.UpdateValues.ChildCollection.Add(timestampValue);
            }

            if (lastModifierCol != null) 
            {
                //最后修改人ID
                var lastModifierField = new UpdateField();
                lastModifierField.IsUseFieldPrefix = false;
                lastModifierField.IsUseVarBinding = true;
                lastModifierField.Table = updateSql.SqlBuildingInfo.CurrentSqlTable;
                lastModifierField.FieldName = lastModifierCol.ColumnName;
                updateSql.UpdateFields.ChildCollection.Add(lastModifierField);

                var lastModifierValue = new UpdateValue();
                lastModifierValue.Value = UserUtil.GetCurrentUserID();
                lastModifierValue.DataType = (int)lastModifierEle.DataType;
                lastModifierValue.DbColumnDataType = lastModifierCol.GetDatabaseColumnType();
                updateSql.UpdateValues.ChildCollection.Add(lastModifierValue);
            }

            if (lastModifierNameCol != null) 
            {
                //最后修改人ID
                var lastModifierNameField = new UpdateField();
                lastModifierNameField.IsUseFieldPrefix = false;
                lastModifierNameField.IsUseVarBinding = true;
                lastModifierNameField.Table = updateSql.SqlBuildingInfo.CurrentSqlTable;
                lastModifierNameField.FieldName = lastModifierNameCol.ColumnName;
                updateSql.UpdateFields.ChildCollection.Add(lastModifierNameField);

                var lastModifierNameValue = new UpdateValue();
                lastModifierNameValue.Value = UserUtil.GetCurrentUserName();
                lastModifierNameValue.DataType = (int)lastModifierNameEle.DataType;
                lastModifierNameValue.DbColumnDataType = lastModifierNameCol.GetDatabaseColumnType();
                updateSql.UpdateValues.ChildCollection.Add(lastModifierNameValue);
            }
        }

        /// <summary>
        /// 构造UpdateSql的过滤条件。
        /// </summary>       
        /// <param name="sql">Sql语句对象。</param>
        /// <param name="context">Sql构造上下文信息。</param>
        private void HandlingConditionInfoAddData(UpdateSqlStatement sql, SqlBuildingContext context)
        {
            base.GetUpdateSqlCondition(sql, context, context.Node, context.DataObject);
        }
    }
}
